﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Pg.BioMedics.SDR.Entities;

namespace Pg.BioMedics.SDR.Services.Algorithms
{
    /// <summary>
    /// Implementatio of Tf-Idf (Term frequency, inverse document frequency algorithm)
    /// </summary>
    internal class WeightingTfIdfAlgorithm : IWeightingAlgorithm
    {
        #region Fields

        private double lc;
        private int nc;
        private Func<Token, int> nct;

        #endregion

        #region Constructors
        
        /// <summary>
        /// Creates instance of the Okapi formula implementation
        /// </summary>
        /// <param name="lc">Mean document transcription length</param>
        /// <param name="nc">total number of documents in the collection</param>
        /// <param name="nct">formula evaluating number of the documents containing t</param>
        internal WeightingTfIdfAlgorithm(double lc, int nc, Func<Token, int> nct)
        {
            this.lc = lc;
            this.nc = nc;
            this.nct = nct;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets / sets value of the Lc parameter of the Okapi formula.
        /// Lc is a mean document transcription length.
        /// </summary>
        internal double Lc
        {
            get
            {
                return this.lc;
            }
        }

        /// <summary>
        /// Gets / sets Nc parameter value. 
        /// Nc defines total number of documents in the collection.
        /// </summary>
        internal int Nc
        {
            get
            {
                return this.nc;
            }
        }

        /// <summary>
        /// Gets / sets delegate used for nc(t) formula evaluation
        /// </summary>
        internal Func<Token, int> Nct
        {
            get
            {
                return this.nct;
            }
        }

        #endregion

        #region IWeightingAlgorithm Members

        /// <summary>
        /// Calculates query weights vector value for given term t
        /// </summary>
        /// <param name="t">Term</param>
        /// <param name="Q">Query terms vector</param>
        /// <returns>q(t) - component of weight vector</returns>
        public double CalculateQWeight(Token t, Token[] Q)
        {
            return Math.Log10(1.0d + Ft(t, Q)) * Math.Log10((double)this.nc / (double)this.nct(t));   
        }

        /// <summary>
        /// Calculates weight of term t according to the documents D term set
        /// </summary>
        /// <param name="t">Term t</param>
        /// <param name="D">Document terms vector</param>
        /// <returns>d(t) - component of weight vector</returns>
        public double CalculateDWeight(Token t, Token[] D)
        {
            return Math.Log10(1.0d + Ft(t, D));  
        }

        #endregion

        #region Support methods

        /// <summary>
        /// Calculates inverted density formula for the given token t
        /// </summary>
        /// <param name="t">Token from the query</param>
        /// <returns>IDF value</returns>
        private double IDF(Token t)
        {
            int nct = Nct(t);
            return ((double)(this.nc - nct) + 0.5d) / ((double)nct + 0.5d);
        }

        /// <summary>
        /// Calculates token frequency in the 
        /// </summary>
        /// <param name="t">Token t</param>
        /// <param name="T">Tokens set</param>
        /// <returns>Number of token occurances</returns>
        private double Ft(Token t, Token[] T)
        {
            double f = 0.0d;
            for (int i = 0; i < T.Length; i++)
                if (T[i].Value == t.Value)
                    f++;

            return f;
        }

        #endregion
    }
}
